Skip to content

feat(auth): log in with Auth0's device flow - #469

Merged
lihbr merged 14 commits into
mainfrom
lihbr/device-flow
Nov 13, 2026
Merged

lihbr merged 14 commits into
mainfrom
lihbr/device-flow

Conversation

@lihbr

@lihbr lihbr commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

With Auth0's device flow, prismic login and prismic init print a link and a code, and you log in by approving them in any browser.

Plan: https://app.notion.com/p/40e3bee086698197b150c01b9f8a4c78

Step What it adds PR Status
C1 Device flow and Auth0 settings per host #467 Merged
C2 Login storage and lock #468 Merged
C3 Token refresh #473 Merged
C4 Commands switch to the device flow #480 Merged
C5 Logout ends the session #470 Merged
C6 Profile check after login #471 Merged
C7 Login details on the existing End event #472 Merged
C8 Sends the token to /validate in a header. Merges once the backend accepts it. #479 Merged
C9 prismic login --help text #481 Closed, its one line moves into C4
C4 fix Ctrl-C after a login saved no longer says "Login cancelled." #484 Merged
C10 CLI version and OS in Auth0's login records, for the dashboard's profile page #485 Merged
D1 prismic.io docs prismicio/prismic-docs-v4#3999 Draft, merges on release day

This branch has Angelo's #445 (one client file) and #478 (log in again when a token is refused) from main. C4 switches #478's login to the device flow.

Merge with a merge commit, not a squash, so each step's commit reaches the changelog.

🤖 Generated with Claude Code


Note

High Risk
Overhauls authentication, token storage, encryption, and concurrent login/logout behavior—security-critical paths for every CLI command.

Overview
Replaces the old localhost callback login with Auth0’s device flow: prismic login / prismic init print a verification URL and user code, poll until approval, and store access + refresh tokens with automatic renewal on 401s (no more detached refresh subprocess).

Adds a credentials layer that persists sessions in the OS keyring when possible (macOS security, elsewhere @napi-rs/keyring) or falls back to a versioned credentials file, optionally AES-encrypted (credentials.enc), with file locks so parallel CLI processes can resume or coordinate logins. Logout now revokes the refresh token and clears stored secrets while keeping the host.

Auth0 requests carry User-Agent: prismic-cli/<version> (<platform>) for dashboard attribution. Telemetry records login outcome, store mode, and revocation. Tests use a fake Auth0 server and fixtures; CI adds a macOS job for login-related tests that depend on keychain behavior.

Reviewed by Cursor Bugbot for commit 30624f0. Bugbot is set up for automated code reviews on this repo. Configure here.

lihbr and others added 12 commits November 11, 2026 15:10
Every device flow step merges here. This branch merges into main at
release time, with a merge commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the building blocks for device flow login. Nothing calls them yet: src/lib/auth0.ts (the device code and token requests, polling, DeviceFlowError), the host to Auth0 tenant table in src/auth.ts, and a fake Auth0 server with temporary tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
finishLogin() forgets the tracked user and reads /user/profile once,
which also creates the Prismic user for a new social or SSO signup. It
returns the email for C4's "Logged in" message. Nothing calls it yet.

Co-authored-by: lihbr <lihbr@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Adds the credential store for device flow logins. Nothing calls it yet: src/credentials.ts (one keychain key per config directory, the encrypted credentials.enc, a plaintext fallback whose reason is kept for telemetry, and older credential files), src/lib/keyring.ts (/usr/bin/security on macOS, @napi-rs/keyring elsewhere, now a dependency) and src/lib/lock.ts (the credentials.lock directory lock), with temporary tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

Adds login details to the "Prismic CLI End" event instead of new events. Nothing sets them yet: store and storeReason on every command (initTracking), loginResult, loginResumed and loginDurationS after a login (trackLogin), and revoked after a logout (trackLogout). getLoginResult maps DeviceFlowError reasons onto loginResult. Start and End stay the same until C4 calls these, with temporary tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds token refresh for device flow logins. Nothing calls it yet: getSessionToken() in src/auth.ts (returns the saved token while it has more than 5 minutes left, otherwise refreshes under the credentials lock with the tenant saved with the session, ends the session on invalid_grant, and keeps the current token while Auth0 can't be reached) and refreshAccessToken() in src/lib/auth0.ts, with temporary tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds revokeRefreshToken() to src/lib/auth0.ts, and endSession() and replaceSession() to src/auth.ts. endSession() revokes the session at the Auth0 tenant that issued it, then deletes the saved login, the keychain key and the tracked user, keeps the host, and returns { revoked, warnings }. replaceSession() saves a new login, then revokes the previous session. Nothing calls them until C4. Temporary tests use the fake Auth0 server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI put the token in the /validate URL as ?token=, where logs can capture it. It now sends it as an Authorization: Bearer header, like the other Prismic requests. With no token, it still sends no credentials. Needs authentication-server#123, in prod since Nov 13.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`prismic login`, `prismic init` and the automatic login use Auth0's device flow. The CLI shows a link and a code to approve on any device, so remote machines and AI agents can log in. An agent stops after 90 seconds, and its next run continues the same code. Logins go to the new keychain-backed store, refresh under a lock, and logout revokes them at Auth0. The localhost login server and the old background refresh are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lihbr
lihbr marked this pull request as ready for review November 13, 2026 02:50

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5eface2. Configure here.

Comment thread src/auth.ts Resolved
Comment thread src/auth.ts Resolved
lihbr and others added 2 commits November 13, 2026 12:16
…484)

A Ctrl-C while a new login was being saved printed "Login cancelled." and recorded a cancel, although the login had saved. The save keeps the credentials lock while it revokes the previous session, and the Ctrl-C handler waited for that lock. The handler now checks whether the login saved, prints "Logged in to Prismic." and records a success. It still exits 130.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The four Auth0 requests now send `User-Agent: prismic-cli/<version> (<platform>)`. Auth0 keeps it with each login, so the dashboard's profile page can show which CLI version and system each login came from. Requests to Prismic keep the plain `prismic-cli`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lihbr
lihbr merged commit a2a2e89 into main Nov 13, 2026
18 checks passed
@lihbr
lihbr deleted the lihbr/device-flow branch November 13, 2026 03:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant